/*
 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* 本文件主要用来封装车辆检测代码，以及实现上层功能，
 * 后期车辆堵塞判断的功能在该层实现
 *
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

// sample_comm_nnie.h 主要包含了nnie硬件层面部署设置函数
// sample_media_ai.h yolo cnn检测的核心代码接口
// ai_infer_process.h cnn和yolo软体层面实现方式 ======= 固定代码不用改
// yolov2_car_detect.h 基本的车辆检测实现流程 ======= 原文件为手部检测，需更改为车辆检测
// vgs_img.h 包含基本的缩放、裁剪、画框的函数
// ive_img.h 将frame转化为img然后做相应处理
// misc_util.h 按比例转换坐标的函数，box映射回源图时需要
// hisignalling.h 与3861通信时使用
#include "my_car_detect.h"
#include "sample_comm_nnie.h"
#include "vgs_img.h"
#include "ive_img.h"
#include "misc_util.h"
#include "my_hisignalling.h"
#include "sample_comm_svp.h"

#include "myconfig.h"
#include "my_sample_media_ai.h" 
#include "my_yolo_process.h"//#include "ai_infer_process.h"
#include "my_region_manage.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* End of #ifdef __cplusplus */


// 定义最大允许检测的数目
#define DETECT_OBJ_MAX     40
// 画框时框的宽度
#define DRAW_RETC_THICK    8    // Draw the width of the line

// 待检测的视频流将会保存在img中 = 待检测的图片
static IVE_IMAGE_S img;
static IVE_IMAGE_S imgIn;
// objs yolo检测到的所有目标都会保存在这里，每一帧开始时自动清空
static DetectObjInfo objs[DETECT_OBJ_MAX] = {0};
// boxs 保存yolo检测到的目标对象的坐标信息
// static RectBox boxs[DETECT_OBJ_MAX] = {0};
static RectBox carBoxs[DETECT_OBJ_MAX] = {0};
static RectBox anchorBoxs[10] = {0};

static int count = 0;
static unsigned char flag0 = 0;
static unsigned char flag1 = 0;
static unsigned char flag2 = 0;

int myUartFd = 0;

static RectBox regionBoxes[8] = {0};

// my para
static RectBox cropBox = {111, 719, 0, 608};
static RectBox cropBoxShow = {222, 1438, 0, 1080};
static RectBox cropBoxShowArr[1] = {0};
// 背景图片的中心点在（(608/2+111)*2,1080/2）
static const int centralPoint[2] = {(608/2+111)*2,1080/2};

static int anchorIdx[4] = {0};

static int anchorsNum = 0;
static int carsNum = 0;
 

#pragma region LoadModel
HI_S32 YoloCarDetectLoad(uintptr_t* model)
{
    // 初始化车辆检测模型
    SAMPLE_SVP_NNIE_CFG_S *self = NULL;
    HI_S32 ret;

    #ifdef YOLOV3_H
        ret = Yolo3Create(&self, MODEL_FILE_YOLOV3);
    #else
        #ifdef YOLOV2_H
            ret = Yolo2Create(&self, MODEL_FILE_YOLOV2);
        #endif
    #endif
    
    *model = ret < 0 ? 0 : (uintptr_t)self;
    if (ret == 0){
        SAMPLE_PRT("Load YOLO model success\n");
    }
    /* uart open init */ 
    // uart用来与3861通信
    myUartFd = UartOpenInit();
    if (myUartFd < 0) {
        printf("uart1 open failed\r\n");
    } else {
        printf("uart open successed\r\n");
    }
    // MyGetRegion(regionBoxes);
    cropBoxShowArr[0] = cropBoxShow;

    return ret;
}
#pragma endregion

#pragma region UnloadModel
HI_S32 YoloCarDetectUnload(uintptr_t model)
{
    #ifdef YOLOV3_H
        Yolo3Destory((SAMPLE_SVP_NNIE_CFG_S*)model); // Uninitialize the car detection model
    #else
        #ifdef YOLOV2_H
        Yolo2Destory((SAMPLE_SVP_NNIE_CFG_S*)model); // Uninitialize the car detection model
        #endif
    #endif
    SAMPLE_PRT("Unload car detect claasify model success\n");
    return 0;
}
#pragma endregion

#pragma region BoxOffset
static void BoxOffset(RectBox* box){
    int offset = cropBox.xmin;
    box->xmin = box->xmin + offset;
    box->xmax = box->xmax + offset;
}
#pragma endregion

#pragma region DecodeBox
static void DecodeBox(DetectObjInfo objInfo[], const int objNum, const int dstWidth, const int dstHeight){
    anchorsNum = 0;
    carsNum = 0;
    for (int i = 0; i < objNum; i++){
        RectBox box = objInfo[i].box;
        BoxOffset(&box);
        RectBoxTran(&box, MY_RESIZE_WIDTH, MY_RESIZE_HEIGHT, dstWidth, dstHeight);

        if (objInfo[i].cls == 1){
            anchorBoxs[anchorsNum] = box;
            anchorsNum ++;
        }
        else if(objInfo[i].cls == 2){
            carBoxs[carsNum] = box;
            carsNum ++;
        }
    }
}
#pragma endregion

#pragma region AnchorSort
static void AnchorSort(){
    for(int i = 0; i < anchorsNum; i++){
        if (anchorBoxs[i].ymin < centralPoint[1]){
            if(anchorBoxs[i].xmin < centralPoint[0]){
                anchorIdx[0] = i;
            }else{
                anchorIdx[1] = i;
            }            
        }
        else{
            if(anchorBoxs[i].xmin < centralPoint[0]){
                anchorIdx[2] = i;
            }else{
                anchorIdx[3] = i;
            }
        }
    }
}
#pragma endregion

// 检测车辆的函数：输入第一个参数为cnn的model模型，第二个参数为缩放后的图像，第三个参数为原始大小的图像，最终要在dstFrm中绘制方框
// 去掉了形参model
HI_S32 YoloCarDetectCal(uintptr_t model, VIDEO_FRAME_INFO_S *srcFrm, VIDEO_FRAME_INFO_S *dstFrm)
{
    SAMPLE_SVP_NNIE_CFG_S *self = (SAMPLE_SVP_NNIE_CFG_S*)model;
    int objNum;
    int ret;
    int num = 0;
    int regionArr[REGIONNUM] = {0};
    unsigned char regionAbstract[REGIONNUM] = {0};

    ret = FrmToOrigImg((VIDEO_FRAME_INFO_S*)srcFrm, &imgIn);
    ret = ImgYuvCrop(&imgIn, &img, &cropBox);
    SAMPLE_CHECK_EXPR_RET(ret != HI_SUCCESS, ret, "car detect for YUV Frm to Img FAIL, ret=%#x\n", ret);
    
    #ifdef YOLOV3_H
        ret = Yolo3CalImg(self, &img, objs, DETECT_OBJ_MAX, &objNum);// Send IMG to the detection net for reasoning //-->resoning：推理，推理的结果保存在objs（静态变量）中
    #else
        #ifdef YOLOV2_H
        ret = Yolo2CalImg(self, &img, objs, DETECT_OBJ_MAX, &objNum);// Send IMG to the detection net for reasoning //-->resoning：推理，推理的结果保存在objs（静态变量）中
        #endif
    #endif
    
    if (ret < 0) {
        SAMPLE_PRT("Car detect Yolo2CalImg FAIL, for cal FAIL, ret:%d\n", ret);
        return ret;
    }

    // 筛选出车和anchor, 并映射到原始图片大小
    DecodeBox(objs, objNum, dstFrm->stVFrame.u32Width, dstFrm->stVFrame.u32Height);

    if(anchorsNum == 4){
        // 为anchor 编号
        AnchorSort();
        MyGetRegion2(anchorBoxs, anchorIdx, regionBoxes); 
        // DEBUG 绘制车道范围的框 —— 2
        MppFrmDrawRects(dstFrm, regionBoxes, 8, RGB888_RED, DRAW_RETC_THICK);
        MyRegionCount2(carBoxs, regionBoxes, carsNum, regionArr);

        // 车辆数目统计 —— 2
        
        SAMPLE_PRT("======= car detect info ====== \n");
        for(int i = 0; i<REGIONNUM; i++){
            SAMPLE_PRT("region[%d]: %d cars \n", i, regionArr[i]);
        }
        SAMPLE_PRT("total car number: %d\n",carsNum);
        SAMPLE_PRT("==== end info ====\n");
        MyRegionAbstract2(regionArr, regionAbstract);
        

        // 发送信息 —— 3  
        unsigned int msgLen = sizeof(regionAbstract)/sizeof(regionAbstract[0]);
        MyUartSend(myUartFd, regionAbstract, msgLen);
    }
    else{
        SAMPLE_PRT("anchors number wrong.");
    }

    // 画框，绘制车辆识别结果 —— 1
    MppFrmDrawRects(dstFrm, carBoxs, carsNum, RGB888_GREEN, DRAW_RETC_THICK);
    // 绘制anchor框 —— 2
    MppFrmDrawRects(dstFrm, anchorBoxs, anchorsNum, RGB888_GREEN, DRAW_RETC_THICK);
    MppFrmDrawRects(dstFrm, &cropBoxShowArr, 1, RGB888_RED, DRAW_RETC_THICK);

    IveImgDestroy(&img);
    return ret;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* End of #ifdef __cplusplus */

